package middleware

import (
	"gitee.com/bobo-rs/idea-space-framework/consts"
	"gitee.com/bobo-rs/idea-space-framework/enums"
	"gitee.com/bobo-rs/idea-space-framework/framework/model"
	"gitee.com/bobo-rs/idea-space-framework/framework/service"
	"gitee.com/bobo-rs/idea-space-framework/pkg/config"
	"gitee.com/bobo-rs/idea-space-framework/pkg/exception"
	"gitee.com/bobo-rs/idea-space-framework/pkg/response"
	"gitee.com/bobo-rs/idea-space-framework/pkg/utils"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"regexp"
	"strings"
)

const (
	tokenLookupSep = `,`
	tokenMethodSep = `:`
)

// Auth 用户授权认证-第一层
func (m *sMiddleware) Auth(r *ghttp.Request) {
	if err := m.authHandler(r); err != nil {
		response.JsonExit(r, gerror.Code(err).Code(), err.Error())
	}
	// 继续执行
	r.Middleware.Next()
}

// AuthWithe 白名单-用户授权认证（某些特定场景下，允许用户在不登录的情况下访问接口）
func (m *sMiddleware) AuthWithe(r *ghttp.Request) {
	_ = m.authHandler(r)
	// 继续执行
	r.Middleware.Next()
}

// AdminAuth 管理员认证-必须在Auth中间件之后（第二层）
func (m *sMiddleware) AdminAuth(r *ghttp.Request) {
	// 管理员认证
	err := service.User().AuthLoginAdminUser(r.Context())
	if err != nil {
		response.JsonExit(r, gerror.Code(err).Code(), err.Error())
	}

	// 继续执行
	r.Middleware.Next()
}

// AdminAuthApi 管理员接口鉴权-必须在AdminAuth中间件之后（第三层）
func (m *sMiddleware) AdminAuthApi(r *ghttp.Request) {
	// 接口鉴权
	err := m.adminAuthApiHandler(r)
	if err != nil {
		response.JsonExit(r, gerror.Code(err).Code(), err.Error())
	}

	// 继续执行
	r.Middleware.Next()
}

// WriteOperateLog 写入操作日志
func (m *sMiddleware) WriteOperateLog(r *ghttp.Request) {
	r.Middleware.Next()
	// 写入操作日志
	if err := m.writeLogHandler(r, enums.LogOperateTypeOperate); err != nil {
		g.Log().Debugf(r.Context(), `记录日志失败：`, err)
	}
}

// GetToken 获取Token
func (m *sMiddleware) GetToken(r *ghttp.Request) (string, error) {
	item, err := config.New().GetJWTConfigDefault(r.Context())
	if err != nil {
		return "", err
	}
	var (
		tokenLookup = strings.Split(item.TokenLookup, tokenLookupSep)
		token       string
	)

	// Token获取方式
	if len(tokenLookup) == 0 {
		return "", exception.New(`未配置Token方式`)
	}
	for _, tokenLook := range tokenLookup {
		methods := strings.Split(tokenLook, tokenMethodSep)
		if len(methods) < 2 {
			continue
		}
		key := strings.TrimSpace(methods[1])
		// 获取TOKEN
		switch strings.ToUpper(methods[0]) {
		case consts.HttpMethodHeader:
			token = r.GetHeader(key)
		case consts.HttpMethodPost:
			token = r.PostFormValue(key)
		case consts.HttpMethodGet:
			token = r.Get(key).String()
		case consts.HttpMethodCookie:
			token = r.Cookie.Get(key).String()
		default:
		}
		// 直接返回
		if len(token) > 0 {
			break
		}
	}
	if len(token) == 0 {
		return "", exception.NewCode(enums.ErrorNotLogoutIn, ``)
	}
	return strings.TrimSpace(strings.TrimPrefix(token, item.TokenHeadName)), nil
}

// authHandler TOKEN授权处理
func (m *sMiddleware) authHandler(r *ghttp.Request) error {
	token, err := m.GetToken(r)
	if err != nil {
		return err
	}
	// 解析登录TOKEN
	return service.User().LoginAuthParseToken(r.Context(), token)
}

// adminAuthApiHandler 管理员接口鉴权处理
func (m *sMiddleware) adminAuthApiHandler(r *ghttp.Request) error {
	// 管理员信息
	admAuth := service.User().GetAdminUser(r.Context())
	if admAuth == nil {
		return exception.NewCode(enums.ErrorForbidden, `禁止访问`)
	}

	// 是否超管，超管免授权
	if enums.AdminIsSuperManage(admAuth.IsSuperManage) == enums.AdminIsSuperManageSuper {
		return nil
	}

	// 普管鉴权
	menuActionCode, err := service.Rbac().AuthAdminMenuActionCode(r.Context())
	if err != nil {
		return err
	}

	// 接口鉴权
	if _, ok := menuActionCode.ActionApi[utils.Slugify(r.URL.Path)]; !ok {
		return exception.NewCode(enums.ErrorForbidden, ``)
	}
	return nil
}

// writeLogHandler 写入日志记录处理
func (m *sMiddleware) writeLogHandler(r *ghttp.Request, op enums.LogOperateType) error {
	// 解析响应资源，详情和列表不记录响应资源
	var responseText string
	if regexp.MustCompile(`(detail|list)+`).MatchString(r.URL.Path) {
		responseText = r.Response.BufferString()
	}

	// 保存记录
	return service.SysLog().RecordLog(r.Context(), op, model.RecordLogItem{
		Content:      utils.Summary(r),
		ApiUrl:       r.URL.RequestURI(),
		ResponseText: responseText,
	})
}
